﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Axe.Threading
{
    public delegate void  ConcurentForDelegate(int index);
    public delegate void ConcurentForDelegateD(double index);

    public static class Concurent
    {
        public static int numThreads;
        static InternalDelegate InternalDelegateInstance;
        static InternalDelegateD InternalDelegateDInstance;
        
        static Concurent()
        {
            numThreads = Environment.ProcessorCount;

            InternalDelegateInstance = ForDelegate;
            InternalDelegateDInstance = ForDelegateD;
        }

        delegate void InternalDelegate(int start, int end, int increment, ConcurentForDelegate worker);
        delegate void InternalDelegateD(double start, double end, double increment, ConcurentForDelegateD worker);

        private static void ForDelegate(int start, int end, int increment, ConcurentForDelegate worker)
        {
            for (int i = start; i < end; i += increment)
            {
                worker(i);
            }
        }

        private static void ForDelegateD(double start, double end, double increment, ConcurentForDelegateD worker)
        {
            for (double i = start; i < end; i += increment)
            {
                worker(i);
            }
        }

        public static void For(double start, double end, double increment, ConcurentForDelegateD worker)
        {
            List<IAsyncResult> waitHandles = new List<IAsyncResult>();  //slow, but easy.
            double lowerBound = start;
            double upperBound = start;

            for (int i = 0; i < numThreads; i++)
            {
                if (i == numThreads - 1)
                    upperBound = end;
                else
                    upperBound += ((end - start) / numThreads);

                IAsyncResult waitHandle = InternalDelegateDInstance.BeginInvoke(lowerBound, upperBound, increment, worker, null, null);
                waitHandles.Add(waitHandle);

                if ((upperBound - start) % increment == 0)
                    lowerBound = upperBound;
                else
                    lowerBound = start + ((((upperBound - start) / increment) + 1) * increment);
            }

            //Wait till all threads exit
            for (int i = 0; i < numThreads; i++)
            {
                waitHandles[i].AsyncWaitHandle.WaitOne();
            }
        }

        public static void For(int start, int end, int increment, ConcurentForDelegate worker)
        {
            List<IAsyncResult> waitHandles = new List<IAsyncResult>();  //slow, but easy.
            int lowerBound = start;
            int upperBound = start;

            for (int i = 0; i < numThreads; i++)
            {
                if (i == numThreads - 1) 
                    upperBound = end;
                else 
                    upperBound += ((end - start) / numThreads);


                IAsyncResult waitHandle = InternalDelegateInstance.BeginInvoke(lowerBound, upperBound, increment, worker, null, null);
                waitHandles.Add(waitHandle);

                if ((upperBound - start) % increment == 0) 
                    lowerBound = upperBound;
                else 
                    lowerBound = start + ((((upperBound - start) / increment) + 1) * increment);
            }

            //Wait till all threads exit
            for (int i = 0; i < numThreads; i++)
            {
                waitHandles[i].AsyncWaitHandle.WaitOne();
            }
        }
    }
}
